/*-
 * ========================LICENSE_START=================================
 * restheart-core
 * %%
 * Copyright (C) 2014 - 2025 SoftInstigate
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =========================LICENSE_END==================================
 */
package org.restheart;

import java.io.IOException;
import java.net.URL;
import java.time.Instant;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Andrea Di Cesare {@literal <andrea@softinstigate.com>}
 */
public class Version {
    private static final Logger LOGGER = LoggerFactory.getLogger(Version.class);

    /**
     *
     * @return VersionHolder.INSTANCE
     */
    public static Version getInstance() {
        return VersionHolder.INSTANCE;
    }

    private final String version;
    private final Instant buildTime;

    private Version() {
        this.version = Version.class.getPackage().getImplementationVersion() == null
                ? null
                : Version.class.getPackage().getImplementationVersion();

        this.buildTime = extractBuildTime();
    }

    /**
     * the RESTHEart version is read from the JAR's MANIFEST.MF file, which is
     * automatically generated by the Maven build process
     *
     * @return the version of RESTHeart or null if it is not packaged
     */
    public String getVersion() {
        return version;
    }

    /**
     * the RESTHEart build time is read from the JAR's MANIFEST.MF file, which
     * is automatically generated by the Maven build process
     *
     * @return the build time defined in the MANIFEST.MF file, or now if it is
     * not packaged
     */
    public Instant getBuildTime() {
        return buildTime;
    }

    /**
     *
     * @return the build time defined in the MANIFEST.MF file, or now if not
     * present
     */
    private Instant extractBuildTime() {
        final Set<Map.Entry<Object, Object>> MANIFEST_ENTRIES = findManifestInfo();

        return MANIFEST_ENTRIES == null
                ? Instant.now()
                : MANIFEST_ENTRIES
                        .stream()
                        .filter(e -> e.getKey().toString().equals("Build-Time"))
                        .map(e -> (String) e.getValue())
                        .filter(d -> d != null)
                        .map(d -> {
                            try {
                                return Instant.parse(d);
                            } catch (Throwable t) {
                                return Instant.now();
                            }
                        })
                        .findFirst()
                        .orElse(Instant.now());
    }

    private static class VersionHolder {

        private static final Version INSTANCE = new Version();

        private VersionHolder() {
        }
    }

    private static Set<Map.Entry<Object, Object>> findManifestInfo() {
        Set<Map.Entry<Object, Object>> result = null;
        try {
            Enumeration<URL> resources = Thread.currentThread().getContextClassLoader()
                    .getResources("META-INF/MANIFEST.MF");
            while (resources.hasMoreElements()) {
                URL manifestUrl = resources.nextElement();
                Manifest manifest = new Manifest(manifestUrl.openStream());
                Attributes mainAttributes = manifest.getMainAttributes();
                String implementationTitle = mainAttributes.getValue("Implementation-Title");
                if (implementationTitle != null && implementationTitle.toLowerCase().startsWith("restheart")) {
                    result = mainAttributes.entrySet();
                    break;
                }
            }
        } catch (IOException e) {
            LOGGER.warn(e.getMessage());
        }
        return result;
    }
}
